#ifndef __M_MCER_H__
#define __M_MCER_H__

#include"util.hpp"
#include"online.hpp"
#include"db.hpp"
#include"room.hpp"
#include<list>
#include<condition_variable>
#include<mutex>
template<class T>//可以匹配时选择不同的类型，方便后续功能更新
class matcher_queue{
    private:
    //实现匹配队列用list为什么不用queue:有可能需要从中间删除数据的可能，queue不能从中间删除
    std::list<T> _list;
    std::condition_variable _con;//条件变量：生消模型，但是这里只是消费者进行限制，因为消费者需要两个数据才能开启，而生产者可以一直生产只要有数据。
    std::mutex _mutex;
    public:
    //加入数据，尾插模拟队列，要唤醒线程
    void push(const T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);

        _list.push_back(data);
        //为什么加入数据就要唤醒线程，因为我们不知道此时队列中有多少data，把所有都唤醒，让其判断是否有2个data的队列。没有的话死循环会继续堵塞
        _con.notify_all();
    }
    //条件变量等待，如果队列元素小于2，或者此时没有数据进入。
    void wait()
    {
       std::unique_lock<std::mutex> lock(_mutex);
       //条件变量用的时候要把锁也带上，条件不符合时解锁并等待条件符合，因为条件符合时，要解锁，并且自动加锁。
        _con.wait(lock);
    }
    //我们的pop和wait是分开的，因为不只是pop会用到wait，所以可以分离开，但数据小于2的时候你不能pop。
    //且pop时因为是一个一个的pop，所以要保证pop出两个时wait才能被唤醒。
    //弹出数据，并且通过参数返回出去
    bool pop(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_list.empty()) return false;
        data = _list.front();
        _list.pop_front();
        return true;
    }
    //删除指定数据（万一有人掉线）
    void remove(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }
    
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }
    size_t size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
};
template<class T>
class matcher{
    private:
    //不同级别的队列
    matcher_queue<T> _q_bronze;
    matcher_queue<T> _q_silver;
    matcher_queue<T> _q_gold;
    //不同级别的线程
    std::thread _th_bronze;
    std::thread _th_silver;
    std::thread _th_gold;
    //需要用到的句柄
    room_manager *_rm;
    user_table *_ut;
    online_manager *_om;
    private:
    //线程处理函数
    void handle_matcher(matcher_queue<T> &mq)
    {
        //1.循环处理
        while(1)
        {
            //当队列数量大于2时，结束阻塞
            while(mq.size() < 2)
            mq.wait();
            //等待成功后pop两个用户出来
            uint64_t uid1,uid2;
            bool ret = mq.pop(uid1);
            //说明用户没找到，那就继续等2个人或者继续取两个人
            if(ret == false)
            {
                continue;
            }
            ret = mq.pop(uid2);
            //如果到达这里说明uid1已经被取出
            if(ret == false)
            {
                //再把uid1放回队列中等待抽取
                this->add(uid1);
                continue;
            }
            //到了这里说明两个用户都获取了，但是要判断是否有玩家掉线
            wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
            if(conn1.get() == nullptr)
            {
                //如果一方掉线，那就把另一方放入队列，因为两个都已经被取出了
                DLOG("用户：%ld掉线",uid1);
                this->add(uid2);
                continue;
            }
            wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
            if(conn2.get() == nullptr)
            {
                DLOG("用户：%ld掉线",uid2);
                this->add(uid1);
                continue;
            }
            //获取到二者的链接后，可以为二者建立房间？
            room_ptr rp = _rm->room_create(uid1,uid2);
            if(rp.get() == nullptr)
            {
                this->add(uid1);
                this->add(uid2);
                DLOG("房间创建失败");
                continue;
            }
            //这里就是房间创建成功，给两个客户端返回信息
            Json::Value rep;
            rep["optype"] = "match_success";
            rep["result"] = true;
            std::string body;
            json_util::serialize(rep,body);
            conn1->send(body);
            conn2->send(body);
        }
    }
    void bronze_handle(){return handle_matcher(_q_bronze);}
    void silver_handle(){return handle_matcher(_q_silver);}
    void gold_handle(){return handle_matcher(_q_gold);}
    public:
    //对句柄和线程初始化
    matcher(room_manager *rm,user_table *ut,online_manager *om)
    :_rm(rm),_ut(ut),_om(om)
    ,_th_bronze(std::thread(&matcher::bronze_handle,this))
    ,_th_silver(std::thread(&matcher::silver_handle,this))
    ,_th_gold(std::thread(&matcher::gold_handle,this))
    {
        DLOG("matcher管理类:%p,初始化完成",this);
    }
    //要能添加用户进入队列
    bool add(uint64_t uid)
    {
        //根据用户id获取信息
        Json::Value user;
        bool ret = _ut->select_user_by_id(uid,user);
        if(ret == false)
        {
            DLOG("获取玩家：%ld,信息失败",uid);
            return false;
        }
        //添加到指定的队列
        int score = user["score"].asInt();
        if(score < 2000)
        _q_bronze.push(uid);
        else if(score >= 3000)
        _q_gold.push(uid);
        else
        _q_silver.push(uid);
        return true;
    }
    //用户退出队列
    bool del(uint64_t uid)
    {
        Json::Value user;
        bool ret = _ut->select_user_by_id(uid,user);
        if(ret == false)
        {
            DLOG("获取玩家：%ld,信息失败",uid);
            return false;
        }
        int score = user["score"].asInt();
        if(score < 2000)
        _q_bronze.remove(uid);
        else if(score >= 3000)
        _q_gold.remove(uid);
        else
        _q_silver.remove(uid);
        return true;
    }
    
};






#endif